
#pragma once

#ifndef qbe_select_cmd_hpp
#define qbe_select_cmd_hpp

#include "../layer/layer.hpp"
#include "../layout/layout.hpp"
#include "./models.hpp"
#include "qlib/all.h"

namespace qbe::select {

// struct SelectPartData;
// struct Rect;
// enum struct DrawBoxDirect;

class SelectCmd {
 public:
  using EntityArray = std::vector<entt::entity>;
  using PartMap = std::unordered_map<entt::entity, SelectPartData>;
  using PartTypeMap = std::unordered_map<entt::entity, PartData>;
  using PartNodes = std::vector<layout::SelectQuad::Node>;
  using GeoTypes = std::tuple<qlib::Path::s_ptr, qlib::Polygon::s_ptr, qlib::Rectangle::s_ptr, qlib::Circle::s_ptr, qlib::Ellipse::s_ptr,
                              qlib::Fan::s_ptr, qlib::Text::s_ptr, qlib::Label::s_ptr, qlib::Pin::s_ptr, qlib::KeyPoint::s_ptr, qlib::Ruler::s_ptr,
                              qlib::ViewArea3D::s_ptr, qlib::SectionArea3D::s_ptr>;

  SelectCmd(qlib::Cell::s_ptr _cell, layer::Manager& _layer_manager) : cell(_cell), layer_manager(_layer_manager){};
  ~SelectCmd(){};

  // 四叉树查询周围对象
  void query_nodes(double x, double y);
  void query_nodes(double x, double y, double w, double h);

  bool get_adsorb(double x, double y, qlib::Vec2d& point, qlib::GeoPartType& type);

  bool get_part_type(double x, double y, PartNodes& res);
  // 部分-查询-鼠标附近，is_ref: 过滤引用，is_point： 过滤点
  bool get_part(double x, double y, PartMap& res, bool is_ref = true, bool is_point = true, bool is_edge = true);
  void get_box_part(double x, double y, double w, double h, EntityArray& res_selects, PartMap& res_parts, DrawBoxDirect direct = DrawBoxDirect::Left,
                    bool is_overall = true);
  PartMap hover_parts() { return m_hover_parts; };
  PartMap select_parts() { return m_select_parts; };
  // SelectPartData select_parts(entt::entity& e) { return m_select_parts[e]; };
  // 部分的预选是否已经被选中
  bool hover_is_select_part();
  // 部分-添加, is_change: 控制是否联动，比如选择边，自动选择边附近的点。
  void add_hover_parts(PartMap& parts, bool is_change = false);
  void add_select_parts(PartMap& parts, bool is_change = false);
  // 对齐使用
  // PartMap algin_hover_parts() { return m_algin_hover_parts; };
  PartMap algin_select_parts() { return m_algin_select_parts; };
  // void algin_add_hover_parts(PartMap& parts);
  void algin_add_select_parts(PartMap& parts);
  void algin_clear_select_parts() {
    m_algin_select_parts.clear();
    m_algin_select_part_render = true;
  };

  // 部分-删除
  void remove_hover_parts(PartMap& parts, bool is_change = false);
  void remove_hover_parts(EntityArray& e_arr);
  void remove_select_parts(PartMap& parts, bool is_change = false);
  void remove_select_part(const entt::entity& e);      // 删除entity对象的所有部分
  void remove_select_parts(const EntityArray& e_arr);  // 删除entity对象的所有部分

  // 部分-清除
  void clear_select_parts();
  void clear_hover_parts();
  void clear_parts();
  // 部分里面过滤全选
  void filter_select_parts(PartMap& parts, EntityArray& res_selects);

  // // 查询选择里面的是否存为全选
  // bool check_select_part_overall(entt::entity e);

  // 全选-查询-包围盒子
  void get_box_select(double x, double y, double w, double h, EntityArray& res, DrawBoxDirect direct = DrawBoxDirect::Left);
  // 全选-查询-鼠标附近
  void get_select(double x, double y, std::multimap<double, entt::entity>& res_border);
  // 全选-添加预选
  void add_hover(const entt::entity& e);
  void add_hovers(const EntityArray& e_arr);
  void add_select(const entt::entity& e, bool is_order = false);
  void add_selects(const EntityArray& e_arr, bool is_order = false);
  // 全选-删除接口
  void remove_select(const entt::entity& e, bool is_order = false);
  void remove_selects(const EntityArray& e_arr, bool is_order = false);
  // 全选-反选接口
  void reverse_selects(const EntityArray& e_arr);
  // 全选-清除接口
  void clear_hover();
  void clear_select();
  // 预选是否已经被选中, 用于移动等功能
  bool hover_is_select();

  // 图层隐藏使用
  void remove_tag(qlib::Tag& tag);
  std::unordered_map<entt::entity, bool> get_select_orders();
  void add_select_order(const entt::entity& e);
  void clear_select_order();

  // 取消所有
  void clear_all() {
    clear_select_parts();
    clear_hover_parts();
    clear_select();
    clear_hover();
    algin_clear_select_parts();
  }

  // 渲染接口
  bool select_render();
  bool hover_render();
  bool select_part_render();
  bool hover_part_render();
  bool algin_select_part_render();
  bool algin_hover_part_render();

  // 清除所有渲染
  void clear_render();

  //
  bool is_overall_part(entt::entity e, SelectPartData& part);
  void overall_part(entt::entity e, SelectPartData& part);
  void overall_parts(PartMap& parts);

  // 设置鼠标选择范围
  void set_mouse_range(double r);

 private:
  bool projectPointToSegment(const qlib::Vec2d& A, const qlib::Vec2d& B, const qlib::Vec2d& P, qlib::Vec2d& Q);
  // 点是否在矩形内
  bool is_point_in_rect(qlib::Vec2d p, const qlib::Vec2dArray& box);
  bool is_points_in_rect(qlib::Vec2dArray& ps, const qlib::Vec2dArray& box);
  bool is_rect_contained(const Rect& a, const Rect& b);
  // bool is_rect_contained(const qlib::Vec2dArray& a, const qlib::Vec2dArray& b);

  vsg::mat4 get_ref_mat(qlib::Reference::s_ptr ref);
  qlib::Vec2d tran_to_cell_point(qlib::Vec2d p, qlib::Reference::s_ptr ref);
  qlib::Vec2d tran_to_ref_point(qlib::Vec2d p, qlib::Reference::s_ptr ref);

  // 添加/删除部分后自动选中边或者点
  void add_part(SelectPartData& src_part, SelectPartData& part, bool is_path = false, bool is_fan = false);
  void add_parts(PartMap& src_parts, PartMap& parts, bool is_change = false);
  void remove_part(SelectPartData& src_part, SelectPartData& part, bool is_path = false, bool is_fan = false);
  void remove_parts(PartMap& src_parts, PartMap& parts, bool is_change = false);

  template <typename T>
  bool get_part(T geo, qlib::Vec2dArray& res) {
    // path
    if constexpr (std::is_same<T, qlib::Path::s_ptr>::value) {
      res = geo->spine.points;
      return true;
    }
    // polygon
    if constexpr (std::is_same<T, qlib::Polygon::s_ptr>::value || std::is_same<T, qlib::ViewArea3D::s_ptr>::value) {
      res = geo->points;
      return true;
    }
    // rect
    if constexpr (std::is_same<T, qlib::Rectangle::s_ptr>::value || std::is_same<T, qlib::SectionArea3D::s_ptr>::value) {
      auto& poly = geo->to_polygon();
      res = poly->points;
      return true;
    }
    // circle
    if constexpr (std::is_same<T, qlib::Circle::s_ptr>::value) {
      auto& box = geo->box;
      res = qlib::box_to_points(box);
      res.pop_back();
      return true;
    }
    // ellipse
    if constexpr (std::is_same<T, qlib::Ellipse::s_ptr>::value) {
      auto& poly = geo->tran_box();
      res = poly->points;
      return true;
    }
    // fan
    if constexpr (std::is_same<T, qlib::Fan::s_ptr>::value) {
      auto poly = geo->to_polygon();
      // res = qlib::connect_end(poly->points);
      res = poly->points;
      return true;
    }
    // label, ex...
    if constexpr (std::is_same<T, qlib::Label::s_ptr>::value || std::is_same<T, qlib::Pin::s_ptr>::value ||
                  std::is_same<T, qlib::KeyPoint::s_ptr>::value || std::is_same<T, qlib::Text::s_ptr>::value) {
      res = qlib::Vec2dArray{geo->origin};
      return true;
    }
    return false;
  }

  template <typename T>
  bool get_part_try(const entt::registry& reg, entt::entity e, qlib::Vec2dArray& res) {
    auto geo_ptr = reg.try_get<T>(e);
    if (geo_ptr) {
      return get_part(*geo_ptr, res);
    }
    return false;
  }
  template <typename... Args>
  bool get_part_multi(const entt::registry& reg, entt::entity e, qlib::Vec2dArray& res, const std::tuple<Args...>&) {
    return (get_part_try<Args>(reg, e, res) || ...);
  }

  template <typename T>
  void query_part(T geo, double x, double y, double w, double h, SelectPartData& part) {
    auto s_quad = layout::SelectQuad::create();
    qlib::Vec2dArray points;
    int type = 0;

    // path
    if constexpr (std::is_same<T, qlib::Path::s_ptr>::value) {
      points = geo->spine.points;
    }
    // polygon
    if constexpr (std::is_same<T, qlib::Polygon::s_ptr>::value || std::is_same<T, qlib::ViewArea3D::s_ptr>::value) {
      points = qlib::connect_end(geo->points);
      type = 1;
    }
    // rect
    if constexpr (std::is_same<T, qlib::Rectangle::s_ptr>::value || std::is_same<T, qlib::SectionArea3D::s_ptr>::value) {
      auto& poly = geo->to_polygon();
      points = qlib::connect_end(poly->points);
      type = 1;
    }
    // circle
    if constexpr (std::is_same<T, qlib::Circle::s_ptr>::value) {
      auto& box = geo->box;
      points = qlib::box_to_points(box);
      type = 1;
    }
    // ellipse
    if constexpr (std::is_same<T, qlib::Ellipse::s_ptr>::value) {
      auto obb = geo->obb();
      points = qlib::connect_end(obb);
      type = 1;
    }
    // fan
    if constexpr (std::is_same<T, qlib::Fan::s_ptr>::value) {
      auto poly = geo->to_polygon();
      points = qlib::connect_end(poly->points);
      type = 1;
    }

    // ruler
    if constexpr (std::is_same<T, qlib::Ruler::s_ptr>::value) {
      points = qlib::Vec2dArray{geo->start, geo->end};
    }
    // label, ex...
    if constexpr (std::is_same<T, qlib::Label::s_ptr>::value || std::is_same<T, qlib::Pin::s_ptr>::value ||
                  std::is_same<T, qlib::KeyPoint::s_ptr>::value || std::is_same<T, qlib::Text::s_ptr>::value) {
      points = qlib::Vec2dArray{geo->origin};
    }

    if (points.size() == 0)
      return;

    layout::SelectQuad::add_points(s_quad, points, qlib::GeoPartType::Point);
    layout::SelectQuad::add_edges(s_quad, qlib::get_edges(points), qlib::GeoPartType::Edge);
    auto nodes = s_quad->query(quadtree::Box<double>(x, y, w, h));
    part.points = points;
    if (type == 1) {
      part.points.pop_back();
    }

    for (auto& n : nodes) {
      if (n.edge.size() > 0) {
        part.edge_id_map[n.id] = true;
      } else {
        part.point_id_map[n.id] = true;
      }
    }
  }

  template <typename T>
  bool query_part_try(const entt::registry& reg, entt::entity e, double x, double y, double w, double h, SelectPartData& part) {
    auto geo_ptr = reg.try_get<T>(e);
    if (geo_ptr) {
      query_part(*geo_ptr, x, y, w, h, part);
      return true;
    }
    return false;
  }

  template <typename... Args>
  bool query_part_multi(const entt::registry& reg, entt::entity e, double x, double y, double w, double h, SelectPartData& part,
                        const std::tuple<Args...>&) {
    return (query_part_try<Args>(reg, e, x, y, w, h, part) || ...);
  }

  // bool query_close_part(std::vector<qlib::CellQuad::Node>& nodes, double x, double y, PartMap& res, bool enable_ref = true, bool enable_point = true,
  //                       bool enable_edge = true);
  // bool query_close_part(std::vector<qlib::CellQuad::Node>& geo_nodes, OverallModel& model, layout::LayoutCmd& layout_cmd, entt::registry& reg,
  //                       double x, double y, double r, layout::SelectQuad::Node& res, entt::entity& res_entity, bool enable_ref = true,
  //                       bool enable_point = true, bool enable_edge = true);

  bool query_box_part(std::vector<qlib::CellQuad::Node>& geo_nodes, const DrawBoxDirect& direct, double x, double y, double w, double h,
                      std::vector<std::pair<entt::entity, layout::SelectQuad::Node>>& res);

  bool is_intersect(entt::registry& reg, const entt::entity& e, const Rect& box);

 private:
  qlib::Cell::s_ptr cell;

  layer::Manager& layer_manager;
  double range;

  std::vector<qlib::CellQuad::Node> m_nodes;

  // 部分选择
  std::unordered_map<entt::entity, bool> m_select_orders;

  //
  PartMap m_select_parts;  // 部分-选择对象
  PartMap m_hover_parts;   // 部分-预选对象
  bool m_select_part_render{false};
  bool m_hover_part_render{false};

  // 对齐-部分选择
  PartMap m_algin_select_parts;
  PartMap m_algin_hover_parts;
  bool m_algin_select_part_render{false};
  bool m_algin_hover_part_render{false};

  // 选择
  bool m_select_render{false};
  bool m_hover_render{false};
};

}  // namespace qbe::select

#endif